import { IndexVertex } from './IndexVertex';
import { IndexEdge } from './IndexEdge';
import { IndexTriangle } from './IndexTriangle';
import { TVertex } from './TVertex';
import { TEdge } from './TEdge';
import { TTriangle } from './TTriangle';
import { UnorderedSet } from './UnorderedSet';
import { KeyValueMap } from './KeyValueMap';

export class TriMesh {
    //顶点表。Map<IndexVertex,number>
    public vmap: KeyValueMap;
    //边表。Map<TIEdge, number>
    public emap: KeyValueMap;
    //三角形表。Map<IndexTriangle, number>
    public tmap: KeyValueMap;
    //顶点集。
    public vertices: UnorderedSet<TVertex>;
    //边集。
    public edges: UnorderedSet<TEdge>;
    //三角形集。
    public triangles: UnorderedSet<TTriangle>;

    protected initialELabel: number;
    protected initialTLabel: number;

    /**
     * 构造函数。
     */
    public constructor() {
        this.initialELabel = -1;
        this.initialTLabel = -1;
        this.vmap = new KeyValueMap();
        this.emap = new KeyValueMap();
        this.tmap = new KeyValueMap();
    }

    /**
     * 
     * @param label 
     */
    public V(label: number): number {
        var tiv = new IndexVertex(label);
        var ret = this.vmap.get(tiv);
        return ret || -1;
    }

    /**
     * 重置。
     */
    public reset() {
        //vertices
        this.vertices = new UnorderedSet<TVertex>();

        //edges
        this.edges = new UnorderedSet<TEdge>();

        //triangles
        this.triangles = new UnorderedSet<TTriangle>();

    }

    /**
     * 插入一个三角形。
     * 
     * @param label0 三角形顶点索引0。
     * @param label1 三角形顶点索引1。
     * @param label2 三角形顶点索引2。
     */
    public insertTriangle(label0: number, label1: number, label2: number): number {
        var tri = new IndexTriangle(label0, label1, label2);
        var t = -1;//三角形已经存在。
        if (this.tmap.get(tri) === undefined) {
            var m = new TTriangle(this.initialTLabel);
            t = this.triangles.append(m);
            this.tmap.set(tri, t);
        }
        return t;
    }

    /**
     * 插入一个顶点。
     * 
     * @param label 顶点标识。
     */
    public insertVertex(label: number): number {
        var vertex = new IndexVertex(label);
        var v = -1 | 0;
        var iter = this.vmap.get(vertex);
        if (iter !== undefined) {
            v = iter;
        } else {
            var vtex = new TVertex(label);
            v = this.vertices.append(vtex);
            this.vmap.set(vertex, v);
        }
        return v;
    }

    /**
     * 插入一条边。
     */
    public insertEdge(label0: number, label1: number): number {
        var edge = new IndexEdge(label0, label1);
        var e = -1 | 0;
        var iter = this.emap.get(edge);
        if (iter !== undefined) {
            e = iter;
        } else {
            var eg = new TEdge(this.initialELabel);
            e = this.edges.append(eg);
            this.emap.set(edge, e);
        }
        return e;
    }

    /**
     * 插入一个三角面。
     * 
     * @param label0 顶点索引0。
     * @param label1 顶点索引1。
     * @param label2 顶点索引2。
     */
    public insert(label0: number, label1: number, label2: number): boolean {
        var t = this.insertTriangle(label0, label1, label2);
        if (t == -1) {
            return true;
        }
        //插入顶点。
        var v0 = this.insertVertex(label0);
        var v1 = this.insertVertex(label1);
        var v2 = this.insertVertex(label2);
        //插入边。
        var e0 = this.insertEdge(label0, label1);
        var e1 = this.insertEdge(label1, label2);
        var e2 = this.insertEdge(label2, label0);
        //
        var tri = this.triangles.value[t];
        var vt0 = this.vertices.value[v0];
        var vt1 = this.vertices.value[v1];
        var vt2 = this.vertices.value[v2];
        var edge0 = this.edges.value[e0];
        var edge1 = this.edges.value[e1];
        var edge2 = this.edges.value[e2];
        //关联边到顶点上。
        vt0.insertEge(e2);
        vt0.insertEge(e0);
        vt1.insertEge(e0);
        vt1.insertEge(e1);
        vt2.insertEge(e1);
        vt2.insertEge(e2);
        edge0.vertex[0] = v0;
        edge0.vertex[1] = v1;
        edge1.vertex[0] = v1;
        edge1.vertex[1] = v2;
        edge2.vertex[0] = v2;
        edge2.vertex[1] = v0;
        //关联三角形到顶点上。
        vt0.insertTriangle(t);
        vt1.insertTriangle(t);
        vt2.insertTriangle(t);
        tri.vertex[0] = v0;
        tri.vertex[1] = v1;
        tri.vertex[2] = v2;
        //
        this.attachTriangle(t, tri, 0, e0, edge0);
        this.attachTriangle(t, tri, 1, e1, edge1);
        this.attachTriangle(t, tri, 2, e2, edge2);
        return true;
    }

    /**
     * 连接三角形到边上。
     * 
     * @param t 
     * @param tri 
     * @param i 
     * @param e 
     * @param edge 
     */
    public attachTriangle(t: number, tri: TTriangle, i: number, e: number, edge: TEdge): void {
        if (edge.triangle[0] == -1) {
            edge.triangle[0] = t;
        } else {
            var a = edge.triangle[0] | 0;
            var adj = this.triangles.value[a];
            tri.adjacent[i] = a;
            for (var j = 0; j < 3; j++) {
                if (adj.edge[j] == e) {
                    adj.adjacent[j] = t;
                    break;
                }
            }

            if (edge.triangle[1] == -1) {
                edge.triangle[1] = t;
            } else {
                //非流型结构。
                //    AssertUtil.debugTrue(false);
            }
        }
        tri.edge[i] = e;
    }

    /**
     * 获取标签。
     * 
     * @param idx 顶点。
     */
    public getVertexLabel(idx: number): number {
        var vertex = this.vertices.get(idx);
        return vertex.label;
    }

    /**
     * 转成字符串，用于调试。
     */
    public print(): string {
        var text = "";
        //Vertices
        var numVertices = this.vertices.length;
        text += "vertex quantity = " + numVertices + "\n";
        for (var v = 0; v < numVertices; v++) {
            var vertex = this.vertices.value[v];
            text += "vertex<" + v + ">\n";
            text += "    l: " + vertex.label + "\n";
            text += "    e: ";
            var numEdges = vertex.eset.length;
            for (var e = 0; e < numEdges; e++) {
                text += vertex.eset.value[e] + ' ';
            }
            text += "\n";
            text += "    t: ";
            var numTriangles = vertex.tset.length;
            for (var t = 0; t < numTriangles; t++) {
                text += vertex.tset.value[t] + ' ';
            }
            text += "\n";
        }
        text += "\n";
        //Edges
        var numEdges = this.edges.length;
        text += "edge quantity = " + numEdges + "\n";
        for (var e = 0; e < numEdges; e++) {
            var edge = this.edges.value[e];
            text += "edge<" + e + ">\n";
            text += "    v: " + edge.vertex[0] + " " + edge.vertex[1] + "\n";
            text += "    t: " + edge.triangle[0] + " " + edge.triangle[1] + "\n";
        }
        text += "\n";
        //Triangles
        var numTriangles = this.triangles.length;
        text += "triangle quantity = " + numTriangles + "\n";
        for (var t = 0; t < numTriangles; t++) {
            var tri = this.triangles.value[t];
            text += "triangle<" + t + ">\n";
            text += "    v: " + tri.vertex[0] + ' ' + tri.vertex[1] + ' ' + tri.vertex[2] + '\n';
            text += "    e: " + tri.edge[0] + ' ' + tri.edge[1] + ' ' + tri.edge[2] + '\n';
            text += "    a: " + tri.adjacent[0] + ' ' + tri.adjacent[1] + ' ' + tri.adjacent[2] + '\n';
        }
        text += '\n';
        return text;
    }
}